package cn.jinjimi.domain.data.net;

import android.os.Build;

import java.io.IOException;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

import cn.jinjimi.app.AccountManager;
import cn.jinjimi.app.AppContext;
import cn.jinjimi.app.BuildConfig;
import cn.jinjimi.app.bean.Constants;
import cn.jinjimi.app.controller.event.UserAuthenticChangeEvent;
import cn.jinjimi.base.util.AndroidUtils;
import cn.jinjimi.base.util.GsonUtils;
import cn.jinjimi.base.util.LogUtil;
import cn.jinjimi.domain.Dispatcher;
import cn.jinjimi.domain.ServiceManager;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.internal.connection.RouteException;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;


/**
 * API Factory
 */
public class API {

    public static final String JJM_TOKEN_KEY = "JJM-TOKEN";
    public static final String JJM_CLIENT_KEY = "JJM-CLIENT";
    public static final String JJM_CLIENT_USER_AGENT_KEY = "User-Agent";
    public static final String JJM_CLIENT_VALUE = "jjm_app";

    private static volatile Retrofit mRetrofit = null;
    private static Headers mHeaders;

    private static Retrofit getRetrofit() {
        if (mRetrofit == null) {
            synchronized (API.class) {
                if (mRetrofit == null) {
                    OkHttpClient client = new OkHttpClient.Builder()
                            .addInterceptor(newRequestHeaderInterceptor())
                            .addInterceptor(newLoggerInterceptor())
                            .addNetworkInterceptor(newNetworkInterceptor())
                            .connectTimeout(50, TimeUnit.SECONDS)
                            .build();

                    mRetrofit = new Retrofit.Builder()
                            .baseUrl(Constants.Env.API_BASE_URL)
                            .client(client)
                            .addConverterFactory(GsonConverterFactory.create(GsonUtils.getGson()))
                            .addCallAdapterFactory(new APICallAdapterFactory())
                            .build();
                }
            }
        }
        return mRetrofit;
    }

    private static Interceptor newLoggerInterceptor() {
        HttpLoggingInterceptor logger = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
            @Override
            public void log(String message) {
                LogUtil.d("OkHttp", message);
            }
        });
        logger.setLevel(BuildConfig.DEBUG ?
            HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.NONE);
        return logger;
    }

    private static Interceptor newNetworkInterceptor() {
        return new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Response response = null;
                try {
                    response = chain.proceed(chain.request());
                } catch (RouteException e) {
                    //
                }

                if (response != null) {
                    if (response.code() == 401) {

                        removeToken();

                        UserAuthenticChangeEvent event = new UserAuthenticChangeEvent();
                        event.type = UserAuthenticChangeEvent.TYPE_TOKEN_FAIL;
                        ServiceManager.getService(Dispatcher.class).dispatch(event);
                    }

                }

                return response;
            }
        };
    }

    private static Interceptor newRequestHeaderInterceptor() {

        return new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Response response = null;
                try {
                    Request request = chain.request();
                    Request newRequest = request.newBuilder()
                            .headers(getHeaders(request.headers()))
                            .build();
                    response = chain.proceed(newRequest);

                } catch (Exception e) {
                    e.printStackTrace();
                }
                if (response == null) {
                    throw new IOException();
                }
                return response;
            }
        };
    }

    private static Headers getHeaders() {
        return getHeaders(null);
    }

    private static Headers getHeaders(Headers otherHeaders) {
        if (mHeaders == null) {
            mHeaders = Headers.of(getCommonHeadersMap());
        }
        if (otherHeaders != null && otherHeaders.size() > 0) {
            HashMap<String, String> headers = getCommonHeadersMap();
            for (int i = 0; i < otherHeaders.size(); i++) {
                headers.put(otherHeaders.name(i), otherHeaders.value(i));
            }
            return Headers.of(headers);
        }
        return mHeaders;
    }

    private static HashMap<String, String> getCommonHeadersMap() {
        HashMap<String, String> headers = new HashMap<>();
        headers.put(JJM_CLIENT_USER_AGENT_KEY, buildUserAgent());
        headers.put(JJM_CLIENT_KEY, JJM_CLIENT_VALUE);
        headers.put(JJM_TOKEN_KEY, AccountManager.getInstance().getAccount().token);
        return headers;
    }

    private static Headers putHeader(String key, String value) {
        mHeaders = getHeaders().newBuilder().removeAll(key).add(key, value).build();
        return mHeaders;
    }

    private static Headers removeHeader(String key) {
        mHeaders = getHeaders().newBuilder().removeAll(key).build();
        return mHeaders;
    }


    public static String buildUserAgent() {
        String temple = "JJM(%s;%s;%s)";
        String appVersionName = AndroidUtils.getAppVersionName(AppContext.getInstance());
        String device = Build.MODEL;
        String system = "Android " + Build.VERSION.RELEASE;
        return String.format(temple, appVersionName, device, system);
    }


    public static void changeToken(String token) {
        putHeader(JJM_TOKEN_KEY, token);
    }

    public static void removeToken() {
        removeHeader(JJM_TOKEN_KEY);
    }

    public static <T> T create(Class<T> tClass) {
        return getRetrofit().create(tClass);
    }
}
